package com.huajin.gateway.nmap.service.impl;


import com.huajin.gateway.nmap.domain.AssetDO;
import com.huajin.gateway.nmap.domain.ScanconfigDO;
import com.huajin.gateway.nmap.service.AssetService;
import com.huajin.gateway.nmap.service.ScanconfigService;
import org.apache.commons.lang.ArrayUtils;
import org.nmap4j.Nmap4j;
import org.nmap4j.core.nmap.NMapExecutionException;
import org.nmap4j.core.nmap.NMapInitializationException;
import org.nmap4j.data.NMapRun;
import org.nmap4j.data.host.Address;
import org.nmap4j.data.host.Os;

import org.nmap4j.data.host.Status;
import org.nmap4j.data.host.os.OsClass;
import org.nmap4j.data.host.ports.Port;
import org.nmap4j.data.nmaprun.Host;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.util.*;

@Service
public class NmapService {

    @Autowired
    private AssetService assetService;

    @Autowired
    private ScanconfigService scanconfigService;


    @Scheduled(fixedRate = 5 * 1000)
    public void scheduleScan() {

        Map<String, Object> selectParams = new HashMap<>();
        selectParams.put("offset", 0);
        selectParams.put("limit", 1);
        List<ScanconfigDO> scanconfigDOs = scanconfigService.list(selectParams);
        if (scanconfigDOs.size() != 0) {
            String[] includeIPs = scanconfigDOs.get(0).getIncludeIp().split(";");
            List<String> includeIPList = Arrays.asList(includeIPs);
            String excludeIP = scanconfigDOs.get(0).getExcludeIp();
            List<AssetDO> assetDOs = scanByHostName(includeIPList, excludeIP);
            for (AssetDO assetDo : assetDOs) {
                this.saveAsset(assetDo);
            }

        }
    }

    @Scheduled(fixedRate = 5 * 1000)
    public void scheduleScanAssetDO() {
        List<AssetDO> oldAssetDOs = assetService.list(null);
        List<String> hostList = new ArrayList<>();
        for (AssetDO assetDO : oldAssetDOs) {
            hostList.add(assetDO.getIp());
        }
        List<AssetDO> scanedAssetDOs = scanByHostName(hostList, null);
        // TODO just scan the ip don not the os

        Iterator<AssetDO> assetDOIterator = oldAssetDOs.iterator();
        while (assetDOIterator.hasNext()) {
            AssetDO assetDO = assetDOIterator.next();
            for (AssetDO s : scanedAssetDOs) {
                if (assetDO.getIp().equals(s.getIp())) {
                    assetDOIterator.remove();
                    break;
                }
            }
        }

        for (AssetDO assetDo : oldAssetDOs) {
            System.out.println(assetDo.getIp() + " is  offline");
            assetDo.setStatus("offline");
            assetService.update(assetDo);
        }
    }


    public List<AssetDO> scanByHostName(List<String> includeHostString, String excludeHostString) {
        List<AssetDO> assetDOList = new ArrayList<>();
        try {
            String path = "/usr";
            Nmap4j nmap4j = new Nmap4j(path);
            nmap4j.addFlags("-sV -T5 -O -oX -");
            System.out.println(includeHostString);
            for (String s : includeHostString) {
                nmap4j.includeHosts(s);
            }
            if (excludeHostString != null) {
                nmap4j.excludeHosts(excludeHostString);
            }
            nmap4j.execute();
            System.out.println("start namp scan.");
            if (!nmap4j.hasError()) {

                NMapRun nmapRun = nmap4j.getResult();
                ArrayList<Host> hosts = nmapRun.getHosts();
                for (Host host : hosts) {
                    AssetDO assetDO = new AssetDO();
                    Os os = host.getOs();
                    ArrayList<OsClass> osClass = os.getOsClasses();
                    if (osClass.size() != 0) {
                        assetDO.setVender(osClass.get(0).getVendor());
                        assetDO.setOs(osClass.get(0).getOsfamily());
                    } else {
                        assetDO.setVender("unknown");
                        assetDO.setOs("unknown");
                    }
                    StringBuffer portsBuffer = new StringBuffer();
                    ArrayList<Port> portList = host.getPorts().getPorts();
                    for (Port port : portList) {
                        portsBuffer.append(port.getPortId());
                        portsBuffer.append(",");
                    }
                    assetDO.setPorts(portsBuffer.toString());

                    ArrayList<Address> addresses = host.getAddresses();
                    for (Address address : addresses) {
                        if (address.getAddrtype().equals("ipv4")) {
                            assetDO.setIp(address.getAddr());
                        }
                        if (address.getAddrtype().equals("mac")) {
                            assetDO.setMac(address.getAddr());
                        } else {
                            assetDO.setMac("unknown");
                        }
                    }

                    Status status = host.getStatus();

                    assetDO.setStatus(status.getState());

                    System.out.println(assetDO);
                    assetDOList.add(assetDO);


                }
                String output = nmap4j.getOutput();
                if (output == null) {
                    System.out.println("error");
                }
                String errors = nmap4j.getExecutionResults().getErrors();
                if (errors == null) {
                    System.out.println("error");
                }

            }
        } catch (NMapInitializationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();

        } catch (NMapExecutionException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();

        }
        return assetDOList;

    }


    public void saveAsset(AssetDO assetDO) {

        Map<String, Object> theSameIpParameter = new HashMap<>();
        theSameIpParameter.put("ip", assetDO.getIp());
        List<AssetDO> oldAssetDOs = assetService.list(theSameIpParameter);
        if (oldAssetDOs.size() != 0) {
            //exist the asset get the first one.
            AssetDO oldAssetDO = oldAssetDOs.get(0);
            if (!oldAssetDO.getMac().equals(assetDO.getMac())) {
                System.out.println("the asset finger printer is changed to " + assetDO.getMac());
                oldAssetDO.setMac(assetDO.getMac());
                oldAssetDO.setStatus("up");
                assetService.update(oldAssetDO);
            }
        } else {
            // don not existed.
            int count = assetService.getTheLastId();
            assetDO.setId(count);
            assetDO.setFlushTime(new Date());
            assetDO.setName(assetDO.getVender() + count);
            assetDO.setStatus("up");
            assetService.save(assetDO);
            System.out.println("the new  asset  is added" + assetDO.getIp());
        }
    }

}
